// @ts-check
// axios 中文说明：http://www.axios-js.com/zh-cn/docs/index.html#axios-config
// 接口说明：http://192.168.2.200:8788/api/dev2-frontend/edit/

import axios from 'axios'
import conf from '../config'
import * as auth from './auth'
import { resolvePath } from '.'
import { Notification, Loading } from 'element-ui'

const __DEV__ = process.env.NODE_ENV === 'development'

/**
 * 产出一个 axios 实例
 * @param { Record<string, any> } options 所有选项配置 详见 conf.api
 * @param { Record<string, any> } loadingOptions 加载选项的配置，详见 [Loading 加载](https://element.eleme.cn/#/zh-CN/component/loading)
 * @param { Record<string, any> } notificationOptions 错误弹窗的配置，详见 [Notification 通知](https://element.eleme.cn/#/zh-CN/component/notification)
 */
export function createAxios(options, loadingOptions = {}, notificationOptions = {}) {
  let loadingInstance

  const showErrorModal = (title, message) =>
    Notification.warning(_merge({}, notificationOptions, { title, message }))

  const axiosConfig = {
    baseURL: options.baseURL,
    timeout: options.timeout
  }

  if (options.noCache === true) {
    axiosConfig.headers = { 'Cache-Control': 'no-cache' }
    axiosConfig.params = { _t: +new Date() } // when method includes 'get,delete,head,options'
    axiosConfig.data = { _t: +new Date() } // when method includes 'post,put,patch'
  }

  const _axios = axios.create(axiosConfig)

  _axios.interceptors.request.use(config => {
    if (options.showLoading === true) {
      if (!Reflect.has(loadingOptions, 'text')) {
        loadingOptions.text = options.loadingText
      }
      loadingInstance = Loading.service(loadingOptions)
    }
    // 这段代码是模拟请求前的通用配置
    // 比如这里我模拟处理 Token 值
    // 实际情况根据项目走
    let token = auth.getToken()

    // 模拟 token
    if (!token && __DEV__) {
      token = ''
    }

    if (token) {
      config.headers.Authorization = `Bearer ${ token }`
    }

    _merge(config.headers, options.header)

    return config
  }, error => Promise.reject(error))

  _axios.interceptors.response.use(response => {
    loadingInstance && loadingInstance.close()

    // 这段代码是模拟请求后成功通用配置
    // 比如这里将成功后的返回体重新包装在分发
    // 实际情况根据项目走
    const body = response.data

    if (body.success === false) {
      // 根据实际项目的返回体处理
      showErrorModal(body.message, body.data)

      return Promise.reject({ // 模拟一个 Error 对象
        message: body.data,
        response: response
      })
    }
    return response
  }, error => {
    loadingInstance && loadingInstance.close()

    // 这段代码是模拟请求后失败通用配置
    // 比如这里就是给出个全局的提示
    // 实际情况根据项目走
    showErrorModal('异常提示', error.message)

    return Promise.reject(error)
  })

  return _axios
}

/**
 * 默认配置的 axios 实例
 */
const _axios = createAxios({ ...conf.api })

/**
 * 初始化 API，用于产出使用的方法
 * @param { string | string[] } reservedURL 预设链接
 * @param { object } reservedRest 预设参数
 * @returns Function
 */
export function initAPI(reservedURL = '', reservedRest = {}) {
  reservedURL = Array.isArray(reservedURL) ? reservedURL : [reservedURL]

  /**
   * @param { string } url
   * @param { string } method [get|delete|options|put|post|patch|head]
   * @param { object } rest
   */
  return function(url, method, rest = {}) {
    // @ts-ignore
    reservedURL.push(url)
    url = resolvePath.apply(null, reservedURL)
    rest = { ...reservedRest, ...rest }
    // 移除 axios 自带的 auth 参数，它会覆盖 options.header.Authorization
    // 如果需要重新定义 createAPI 方法，这段代码需要补充
    if (Reflect.has(rest, 'auth')) {
      delete rest.auth
    }

    return _axios({
      url,
      method,
      ...rest
    })
  }
}

export default _axios

/**
 * 创建用于导出数据的表单
 * @param { string } url 注意，这里需要补充完整路径
 * @param { any | any[] } data
 */
export function createExportForm(url, data) {
  const form = document.createElement('form')

  form.method = 'POST' // 只采用 post
  form.action = url

  if (Array.isArray(data)) {
    data.forEach((d) => {
      const input = document.createElement('input')
      input.name = 'ids[]'
      input.value = d
      form.appendChild(input)
    })
  } else {
    const input = document.createElement('input')
    input.name = 'project_id'
    input.value = data
    form.appendChild(input)
  }

  document.body.appendChild(form)
  form.submit()
  document.body.removeChild(form)
}

function _merge() {
  for (let i = 1; i < arguments.length; i++) {
    for (const key in arguments[i]) {
      if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
        arguments[0][key] = arguments[i][key]
      }
    }
  }
  return arguments[0]
}
